Creating a Simple Client-Server Application - Part 2 - with Basic Encryption

In this tutorial, we will create a simple client-server application in C with a basic XOR encryption. The server will listen for incoming connections on a specified port, and the client will connect to the server, send an encrypted message, and receive an encrypted response. This tutorial is aimed at beginners, so we will explain each line of code in detail.

Server Code


// server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8080
#define KEY 0xAA // Simple XOR key

void xor_encrypt_decrypt(char *data, size_t len, char key) {
    for (size_t i = 0; i < len; i++) {
        data[i] ^= key;
    }
}

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[1024] = {0};
    const char *hello = "Hello from server";

    // Creating socket file descriptor
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // Forcefully attaching socket to the port 8080
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("setsockopt");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // Forcefully attaching socket to the port 8080
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    if (listen(server_fd, 3) < 0) {
        perror("listen");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
        perror("accept");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    read(new_socket, buffer, 1024);
    xor_encrypt_decrypt(buffer, strlen(buffer), KEY); // Decrypt the message
    printf("Decrypted message from client: %s\n", buffer);
    xor_encrypt_decrypt(buffer, strlen(buffer), KEY); // Re-encrypt the message for sending back

    char encrypted_hello[1024];
    strcpy(encrypted_hello, hello);
    xor_encrypt_decrypt(encrypted_hello, strlen(encrypted_hello), KEY); // Encrypt the response
    send(new_socket, encrypted_hello, strlen(encrypted_hello), 0);
    printf("Encrypted hello message sent\n");

    close(new_socket);
    close(server_fd);
    return 0;
}

Let's break down the server code:

  • #include <stdio.h>: Includes the standard input/output library.
  • #include <stdlib.h>: Includes the standard library for memory allocation, process control, conversions, etc.
  • #include <string.h>: Includes the string handling library.
  • #include <unistd.h>: Includes the POSIX operating system API.
  • #include <arpa/inet.h>: Includes definitions for internet operations.
  • #define PORT 8080: Defines the port number the server will listen on.
  • #define KEY 0xAA: Defines the XOR key for encryption and decryption.
  • void xor_encrypt_decrypt(char *data, size_t len, char key): Function to encrypt and decrypt data using XOR.
  • int main(): The main function where the program execution begins.
  • int server_fd, new_socket;: Declares file descriptors for the server and new socket.
  • struct sockaddr_in address;: Declares a structure to hold the server address.
  • int opt = 1;: Option for setsockopt to reuse the address and port.
  • int addrlen = sizeof(address);: Length of the address structure.
  • char buffer[1024] = {0};: Buffer to store the message from the client.
  • const char *hello = "Hello from server";: Message to send to the client.
  • if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0): Creates a socket and checks for errors.
  • if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))): Sets socket options to reuse the address and port.
  • address.sin_family = AF_INET;: Sets the address family to IPv4.
  • address.sin_addr.s_addr = INADDR_ANY;: Binds the socket to all available interfaces.
  • address.sin_port = htons(PORT);: Sets the port number, converting it to network byte order.
  • if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0): Binds the socket to the specified address and port.
  • if (listen(server_fd, 3) < 0): Listens for incoming connections with a backlog of 3.
  • if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0): Accepts an incoming connection.
  • read(new_socket, buffer, 1024);: Reads the message from the client into the buffer.
  • xor_encrypt_decrypt(buffer, strlen(buffer), KEY);: Decrypts the message from the client.
  • printf("Decrypted message from client: %s\n", buffer);: Prints the decrypted message from the client.
  • xor_encrypt_decrypt(buffer, strlen(buffer), KEY);: Re-encrypts the message for sending back.
  • char encrypted_hello[1024];: Buffer to store the encrypted response.
  • strcpy(encrypted_hello, hello);: Copies the hello message to the buffer.
  • xor_encrypt_decrypt(encrypted_hello, strlen(encrypted_hello), KEY);: Encrypts the response.
  • send(new_socket, encrypted_hello, strlen(encrypted_hello), 0);: Sends the encrypted response to the client.
  • printf("Encrypted hello message sent\n");: Prints a confirmation message.
  • close(new_socket);: Closes the new socket.
  • close(server_fd);: Closes the server socket.
  • return 0;: Exits the program.

Client Code


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8080
#define KEY 0xAA // Simple XOR key

void xor_encrypt_decrypt(char *data, size_t len, char key) {
    for (size_t i = 0; i < len; i++) {
        data[i] ^= key;
    }
}

int main() {
    int sock = 0, valread;
    struct sockaddr_in serv_addr;
    char *hello = "Hello from client";
    char buffer[1024] = {0};

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("\n Socket creation error \n");
        return -1;
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    // Convert IPv4 and IPv6 addresses from text to binary form
    if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
        printf("\nInvalid address/ Address not supported \n");
        return -1;
    }

    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        printf("\nConnection Failed \n");
        return -1;
    }

    char encrypted_hello[1024];
    strcpy(encrypted_hello, hello);
    xor_encrypt_decrypt(encrypted_hello, strlen(encrypted_hello), KEY); // Encrypt the message
    send(sock, encrypted_hello, strlen(encrypted_hello), 0);
    printf("Encrypted hello message sent\n");

    valread = read(sock, buffer, 1024);
    xor_encrypt_decrypt(buffer, valread, KEY); // Decrypt the response
    printf("Decrypted message from server: %s\n", buffer);

    return 0;
}

Let's break down the client code:

  • #include <stdio.h>: Includes the standard input/output library.
  • #include <stdlib.h>: Includes the standard library for memory allocation, process control, conversions, etc.
  • #include <string.h>: Includes the string handling library.
  • #include <unistd.h>: Includes the POSIX operating system API.
  • #include <arpa/inet.h>: Includes definitions for internet operations.
  • #define PORT 8080: Defines the port number the client will connect to.
  • #define KEY 0xAA: Defines the XOR key for encryption and decryption.
  • void xor_encrypt_decrypt(char *data, size_t len, char key): Function to encrypt and decrypt data using XOR.
  • int main(): The main function where the program execution begins.
  • int sock = 0, valread;: Declares a socket and a variable to store the number of bytes read.
  • struct sockaddr_in serv_addr;: Declares a structure to hold the server address.
  • char *hello = "Hello from client";: Message to send to the server.
  • char buffer[1024] = {0};: Buffer to store the message from the server.
  • if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0): Creates a socket and checks for errors.
  • serv_addr.sin_family = AF_INET;: Sets the address family to IPv4.
  • serv_addr.sin_port = htons(PORT);: Sets the port number, converting it to network byte order.
  • if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0): Converts the IP address from text to binary form and checks for errors.
  • if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0): Connects to the server and checks for errors.
  • char encrypted_hello[1024];: Buffer to store the encrypted message.
  • strcpy(encrypted_hello, hello);: Copies the hello message to the buffer.
  • xor_encrypt_decrypt(encrypted_hello, strlen(encrypted_hello), KEY);: Encrypts the message.
  • send(sock, encrypted_hello, strlen(encrypted_hello), 0);: Sends the encrypted message to the server.
  • printf("Encrypted hello message sent\n");: Prints a confirmation message.
  • valread = read(sock, buffer, 1024);: Reads the response from the server into the buffer.
  • xor_encrypt_decrypt(buffer, valread, KEY);: Decrypts the response.
  • printf("Decrypted message from server: %s\n", buffer);: Prints the decrypted message from the server.
  • return 0;: Exits the program.

Compiling and Running the Code

To compile and run the provided C source code files for the ARM64 architecture, follow these steps:


# Compile the server code
gcc -o server server.c

# Compile the client code
gcc -o client client.c

# Run the server
./server

# Run the client in a separate terminal
./client

This will start the server, which listens on port 8080, and the client will connect to the server, send an encrypted message, and receive a response. The messages will be encrypted and decrypted using the XOR algorithm.

 

 

Check out some other Bands on Bandcamp.com. Crazy Fingers (Vancouver 1991), Flying Butt Pliers, and Hammy Ham Hands.

Proudly powered by a Text Editor, an Sftp client and some Internet Searches.

2024 dispelled.ca end of file.